{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Custom Sequences (Part 2b/c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For this example we'll re-use the Polygon class from a previous lecture on extending sequences.\n",
    "\n",
    "We are going to consider a polygon as nothing more than a collection of points (and we'll stick to a 2-dimensional space).\n",
    "\n",
    "So, we'll need a `Point` class, but we're going to use our own custom class instead of just using a named tuple.\n",
    "\n",
    "We do this because we want to enforce a rule that our Point co-ordinates will be real numbers. We would not be able to use a named tuple to do that and we could end up with points whose `x` and `y` coordinates could be of any type."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "First we'll need to see how we can test if a type is a numeric real type.\n",
    "\n",
    "We can do this by using the numbers module."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import numbers"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This module contains certain base types for numbers that we can use, such as Number, Real, Complex, etc."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance(10, numbers.Number)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance(10.5, numbers.Number)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance(1+1j, numbers.Number)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We will want our points to be real numbers only, so we can do it this way:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance(1+1j, numbers.Real)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance(10, numbers.Real)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance(10.5, numbers.Real)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So now let's write our Point class. We want it to have these properties:\n",
    "\n",
    "  1. The `x` and `y` coordinates should be real numbers only\n",
    "  2. Point instances should be a sequence type so that we can unpack it as needed in the same way we were able to unpack the values of a named tuple."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Point:\n",
    "    def __init__(self, x, y):\n",
    "        if isinstance(x, numbers.Real) and isinstance(y, numbers.Real):\n",
    "            self._pt = (x, y)\n",
    "        else:\n",
    "            raise TypeError('Point co-ordinates must be real numbers.')\n",
    "            \n",
    "    def __repr__(self):\n",
    "        return f'Point(x={self._pt[0]}, y={self._pt[1]})'\n",
    "    \n",
    "    def __len__(self):\n",
    "        return 2\n",
    "    \n",
    "    def __getitem__(self, s):\n",
    "        return self._pt[s]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's use our point class and make sure it works as intended:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "p = Point(1, 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Point(x=1, y=2)"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(p)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1, 2)"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p[0], p[1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "x, y = p"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1, 2)"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x, y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, we can start creatiung our Polygon class, that will essentially be a mutable sequence of points making up the verteces of the polygon."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Polygon:\n",
    "    def __init__(self, *pts):\n",
    "        if pts:\n",
    "            self._pts = [Point(*pt) for pt in pts]\n",
    "        else:\n",
    "            self._pts = []\n",
    "            \n",
    "    def __repr__(self):\n",
    "        return f'Polygon({self._pts})'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's try it and see if everything is as we expect:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "p = Polygon()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Polygon([])"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "p = Polygon((0,0), [1,1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Polygon([Point(x=0, y=0), Point(x=1, y=1)])"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "p = Polygon(Point(0, 0), [1, 1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Polygon([Point(x=0, y=0), Point(x=1, y=1)])"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That seems to be working, but only one minor thing - our representation contains those square brackets which technically should not be there as the Polygon class init assumes multiple arguments, not a single iterable.\n",
    "\n",
    "So we should fix that:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Polygon:\n",
    "    def __init__(self, *pts):\n",
    "        if pts:\n",
    "            self._pts = [Point(*pt) for pt in pts]\n",
    "        else:\n",
    "            self._pts = []\n",
    "            \n",
    "    def __repr__(self):\n",
    "        pts_str = ', '.join(self._pts)\n",
    "        return f'Polygon({pts_str})'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But that still won't work, because the `join` method expects an iterable of **strings** - here we are passing it an iterable of `Point` objects:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "p = Polygon((0,0), (1,1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "sequence item 0: expected str instance, Point found",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32mD:\\Users\\fbapt\\Anaconda3\\envs\\deepdive\\lib\\site-packages\\IPython\\core\\formatters.py\u001b[0m in \u001b[0;36m__call__\u001b[1;34m(self, obj)\u001b[0m\n\u001b[0;32m    691\u001b[0m                 \u001b[0mtype_pprinters\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtype_printers\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    692\u001b[0m                 deferred_pprinters=self.deferred_printers)\n\u001b[1;32m--> 693\u001b[1;33m             \u001b[0mprinter\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mpretty\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    694\u001b[0m             \u001b[0mprinter\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mflush\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    695\u001b[0m             \u001b[1;32mreturn\u001b[0m \u001b[0mstream\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mgetvalue\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\Users\\fbapt\\Anaconda3\\envs\\deepdive\\lib\\site-packages\\IPython\\lib\\pretty.py\u001b[0m in \u001b[0;36mpretty\u001b[1;34m(self, obj)\u001b[0m\n\u001b[0;32m    378\u001b[0m                             \u001b[1;32mif\u001b[0m \u001b[0mcallable\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mmeth\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    379\u001b[0m                                 \u001b[1;32mreturn\u001b[0m \u001b[0mmeth\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcycle\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 380\u001b[1;33m             \u001b[1;32mreturn\u001b[0m \u001b[0m_default_pprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcycle\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    381\u001b[0m         \u001b[1;32mfinally\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    382\u001b[0m             \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mend_group\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\Users\\fbapt\\Anaconda3\\envs\\deepdive\\lib\\site-packages\\IPython\\lib\\pretty.py\u001b[0m in \u001b[0;36m_default_pprint\u001b[1;34m(obj, p, cycle)\u001b[0m\n\u001b[0;32m    493\u001b[0m     \u001b[1;32mif\u001b[0m \u001b[0m_safe_getattr\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mklass\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'__repr__'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;32mNone\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;32mis\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[0mobject\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m__repr__\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    494\u001b[0m         \u001b[1;31m# A user-provided repr. Find newlines and replace them with p.break_()\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 495\u001b[1;33m         \u001b[0m_repr_pprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mp\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcycle\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    496\u001b[0m         \u001b[1;32mreturn\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    497\u001b[0m     \u001b[0mp\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mbegin_group\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'<'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\Users\\fbapt\\Anaconda3\\envs\\deepdive\\lib\\site-packages\\IPython\\lib\\pretty.py\u001b[0m in \u001b[0;36m_repr_pprint\u001b[1;34m(obj, p, cycle)\u001b[0m\n\u001b[0;32m    691\u001b[0m     \u001b[1;34m\"\"\"A pprint that just redirects to the normal repr function.\"\"\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    692\u001b[0m     \u001b[1;31m# Find newlines and replace them with p.break_()\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 693\u001b[1;33m     \u001b[0moutput\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mrepr\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    694\u001b[0m     \u001b[1;32mfor\u001b[0m \u001b[0midx\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0moutput_line\u001b[0m \u001b[1;32min\u001b[0m \u001b[0menumerate\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0moutput\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msplitlines\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    695\u001b[0m         \u001b[1;32mif\u001b[0m \u001b[0midx\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32m<ipython-input-28-aa1e7e862518>\u001b[0m in \u001b[0;36m__repr__\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m      7\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      8\u001b[0m     \u001b[1;32mdef\u001b[0m \u001b[0m__repr__\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 9\u001b[1;33m         \u001b[0mpts_str\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;34m', '\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_pts\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     10\u001b[0m         \u001b[1;32mreturn\u001b[0m \u001b[1;34mf'Polygon(pts_str)'\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mTypeError\u001b[0m: sequence item 0: expected str instance, Point found"
     ]
    }
   ],
   "source": [
    "p"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So, let's fix that:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Polygon:\n",
    "    def __init__(self, *pts):\n",
    "        if pts:\n",
    "            self._pts = [Point(*pt) for pt in pts]\n",
    "        else:\n",
    "            self._pts = []\n",
    "            \n",
    "    def __repr__(self):\n",
    "        pts_str = ', '.join([str(pt) for pt in self._pts])\n",
    "        return f'Polygon({pts_str})'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "p = Polygon((0,0), (1,1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Polygon(Point(x=0, y=0), Point(x=1, y=1))"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ok, so now we can start making our Polygon into a sequence type, by implementing methods such as `__len__` and `__getitem__`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Polygon:\n",
    "    def __init__(self, *pts):\n",
    "        if pts:\n",
    "            self._pts = [Point(*pt) for pt in pts]\n",
    "        else:\n",
    "            self._pts = []\n",
    "            \n",
    "    def __repr__(self):\n",
    "        pts_str = ', '.join([str(pt) for pt in self._pts])\n",
    "        return f'Polygon({pts_str})'\n",
    "    \n",
    "    def __len__(self):\n",
    "        return len(self._pts)\n",
    "    \n",
    "    def __getitem__(self, s):\n",
    "        return self._pts[s]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice how we are simply delegating those methods to the ones supported by lists since we are storing our sequence of points internally using a list!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "p = Polygon((0,0), Point(1,1), [2,2])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Polygon(Point(x=0, y=0), Point(x=1, y=1), Point(x=2, y=2))"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Point(x=0, y=0)"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[Point(x=2, y=2), Point(x=1, y=1), Point(x=0, y=0)]"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p[::-1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's implement concatenation (we'll skip repetition - wouldn't make much sense anyway):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Polygon:\n",
    "    def __init__(self, *pts):\n",
    "        if pts:\n",
    "            self._pts = [Point(*pt) for pt in pts]\n",
    "        else:\n",
    "            self._pts = []\n",
    "            \n",
    "    def __repr__(self):\n",
    "        pts_str = ', '.join([str(pt) for pt in self._pts])\n",
    "        return f'Polygon({pts_str})'\n",
    "    \n",
    "    def __len__(self):\n",
    "        return len(self._pts)\n",
    "    \n",
    "    def __getitem__(self, s):\n",
    "        return self._pts[s]\n",
    "    \n",
    "    def __add__(self, other):\n",
    "        if isinstance(other, Polygon):\n",
    "            new_pts = self._pts + other._pts\n",
    "            return Polygon(*new_pts)\n",
    "        else:\n",
    "            raise TypeError('can only concatenate with another Polygon')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1869044255880 Polygon(Point(x=0, y=0), Point(x=1, y=1))\n",
      "1869044253528 Polygon(Point(x=2, y=2), Point(x=3, y=3))\n"
     ]
    }
   ],
   "source": [
    "p1 = Polygon((0,0), (1,1))\n",
    "p2 = Polygon((2,2), (3,3))\n",
    "print(id(p1), p1)\n",
    "print(id(p2), p2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "result = p1 + p2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1869044256552 Polygon(Point(x=0, y=0), Point(x=1, y=1), Point(x=2, y=2), Point(x=3, y=3))\n"
     ]
    }
   ],
   "source": [
    "print(id(result), result)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, let's handle in-place concatenation. Let's start by only allowing the RHS of the in-place concatenation to be another Polygon:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Polygon:\n",
    "    def __init__(self, *pts):\n",
    "        if pts:\n",
    "            self._pts = [Point(*pt) for pt in pts]\n",
    "        else:\n",
    "            self._pts = []\n",
    "            \n",
    "    def __repr__(self):\n",
    "        pts_str = ', '.join([str(pt) for pt in self._pts])\n",
    "        return f'Polygon({pts_str})'\n",
    "    \n",
    "    def __len__(self):\n",
    "        return len(self._pts)\n",
    "    \n",
    "    def __getitem__(self, s):\n",
    "        return self._pts[s]\n",
    "    \n",
    "    def __add__(self, other):\n",
    "        if isinstance(other, Polygon):\n",
    "            new_pts = self._pts + other._pts\n",
    "            return Polygon(*new_pts)\n",
    "        else:\n",
    "            raise TypeError('can only concatenate with another Polygon')\n",
    "            \n",
    "    def __iadd__(self, pt):\n",
    "        if isinstance(pt, Polygon):\n",
    "            self._pts = self._pts + pt._pts\n",
    "            return self\n",
    "        else:\n",
    "            raise TypeError('can only concatenate with another Polygon')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1869044255600 Polygon(Point(x=0, y=0), Point(x=1, y=1))\n",
      "1869044255656 Polygon(Point(x=2, y=2), Point(x=3, y=3))\n"
     ]
    }
   ],
   "source": [
    "p1 = Polygon((0,0), (1,1))\n",
    "p2 = Polygon((2,2), (3,3))\n",
    "print(id(p1), p1)\n",
    "print(id(p2), p2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {},
   "outputs": [],
   "source": [
    "p1 += p2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1869044255600 Polygon(Point(x=0, y=0), Point(x=1, y=1), Point(x=2, y=2), Point(x=3, y=3))\n"
     ]
    }
   ],
   "source": [
    "print(id(p1), p1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So that worked, but this would not:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "p1 = Polygon((0,0), (1,1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "can only concatenate with another Polygon",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-76-0170f97bf2ba>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mp1\u001b[0m \u001b[1;33m+=\u001b[0m \u001b[1;33m[\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m2\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;36m2\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m(\u001b[0m\u001b[1;36m3\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;36m3\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32m<ipython-input-71-0441976a8455>\u001b[0m in \u001b[0;36m__iadd__\u001b[1;34m(self, pt)\u001b[0m\n\u001b[0;32m     28\u001b[0m             \u001b[1;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     29\u001b[0m         \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 30\u001b[1;33m             \u001b[1;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'can only concatenate with another Polygon'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: can only concatenate with another Polygon"
     ]
    }
   ],
   "source": [
    "p1 += [(2,2), (3,3)]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see we get that type error. But we really should be able to handle appending any iterable of Points - and of course Points could also be specified as just iterables of length 2 containing numbers:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Polygon:\n",
    "    def __init__(self, *pts):\n",
    "        if pts:\n",
    "            self._pts = [Point(*pt) for pt in pts]\n",
    "        else:\n",
    "            self._pts = []\n",
    "            \n",
    "    def __repr__(self):\n",
    "        pts_str = ', '.join([str(pt) for pt in self._pts])\n",
    "        return f'Polygon({pts_str})'\n",
    "    \n",
    "    def __len__(self):\n",
    "        return len(self._pts)\n",
    "    \n",
    "    def __getitem__(self, s):\n",
    "        return self._pts[s]\n",
    "    \n",
    "    def __add__(self, pt):\n",
    "        if isinstance(pt, Polygon):\n",
    "            new_pts = self._pts + pt._pts\n",
    "            return Polygon(*new_pts)\n",
    "        else:\n",
    "            raise TypeError('can only concatenate with another Polygon')\n",
    "            \n",
    "    def __iadd__(self, pts):\n",
    "        if isinstance(pts, Polygon):\n",
    "            self._pts = self._pts + pts._pts\n",
    "        else:\n",
    "            # assume we are being passed an iterable containing Points\n",
    "            # or something compatible with Points\n",
    "            points = [Point(*pt) for pt in pts]\n",
    "            self._pts = self._pts + points\n",
    "        return self"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "p1 = Polygon((0,0), (1,1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "metadata": {},
   "outputs": [],
   "source": [
    "p1 += [(2,2), (3,3)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Polygon(Point(x=0, y=0), Point(x=1, y=1), Point(x=2, y=2), Point(x=3, y=3))"
      ]
     },
     "execution_count": 80,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's implement some methods such as `append`, `extend` and `insert`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Polygon:\n",
    "    def __init__(self, *pts):\n",
    "        if pts:\n",
    "            self._pts = [Point(*pt) for pt in pts]\n",
    "        else:\n",
    "            self._pts = []\n",
    "            \n",
    "    def __repr__(self):\n",
    "        pts_str = ', '.join([str(pt) for pt in self._pts])\n",
    "        return f'Polygon({pts_str})'\n",
    "    \n",
    "    def __len__(self):\n",
    "        return len(self._pts)\n",
    "    \n",
    "    def __getitem__(self, s):\n",
    "        return self._pts[s]\n",
    "    \n",
    "    def __add__(self, pt):\n",
    "        if isinstance(pt, Polygon):\n",
    "            new_pts = self._pts + pt._pts\n",
    "            return Polygon(*new_pts)\n",
    "        else:\n",
    "            raise TypeError('can only concatenate with another Polygon')\n",
    "            \n",
    "    def __iadd__(self, pts):\n",
    "        if isinstance(pts, Polygon):\n",
    "            self._pts = self._pts + pts._pts\n",
    "        else:\n",
    "            # assume we are being passed an iterable containing Points\n",
    "            # or something compatible with Points\n",
    "            points = [Point(*pt) for pt in pts]\n",
    "            self._pts = self._pts + points\n",
    "        return self\n",
    "    \n",
    "    def append(self, pt):\n",
    "        self._pts.append(Point(*pt))\n",
    "        \n",
    "    def extend(self, pts):\n",
    "        if isinstance(pts, Polygon):\n",
    "            self._pts = self._pts + pts._pts\n",
    "        else:\n",
    "            # assume we are being passed an iterable containing Points\n",
    "            # or something compatible with Points\n",
    "            points = [Point(*pt) for pt in pts]\n",
    "            self._pts = self._pts + points\n",
    "            \n",
    "    def insert(self, i, pt):\n",
    "        self._pts.insert(i, Point(*pt))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice how we used almost the same code for `__iadd__` and `extend`?\n",
    "The only difference is that `__iadd__` returns the object, while `extend` does not - so let's clean that up a bit:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Polygon:\n",
    "    def __init__(self, *pts):\n",
    "        if pts:\n",
    "            self._pts = [Point(*pt) for pt in pts]\n",
    "        else:\n",
    "            self._pts = []\n",
    "            \n",
    "    def __repr__(self):\n",
    "        pts_str = ', '.join([str(pt) for pt in self._pts])\n",
    "        return f'Polygon({pts_str})'\n",
    "    \n",
    "    def __len__(self):\n",
    "        return len(self._pts)\n",
    "    \n",
    "    def __getitem__(self, s):\n",
    "        return self._pts[s]\n",
    "    \n",
    "    def __add__(self, pt):\n",
    "        if isinstance(pt, Polygon):\n",
    "            new_pts = self._pts + pt._pts\n",
    "            return Polygon(*new_pts)\n",
    "        else:\n",
    "            raise TypeError('can only concatenate with another Polygon')\n",
    "\n",
    "    def append(self, pt):\n",
    "        self._pts.append(Point(*pt))\n",
    "        \n",
    "    def extend(self, pts):\n",
    "        if isinstance(pts, Polygon):\n",
    "            self._pts = self._pts + pts._pts\n",
    "        else:\n",
    "            # assume we are being passed an iterable containing Points\n",
    "            # or something compatible with Points\n",
    "            points = [Point(*pt) for pt in pts]\n",
    "            self._pts = self._pts + points\n",
    "    \n",
    "    def __iadd__(self, pts):\n",
    "        self.extend(pts)\n",
    "        return self\n",
    "    \n",
    "    def insert(self, i, pt):\n",
    "        self._pts.insert(i, Point(*pt))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's give all this a try:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 93,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1869044425392 Polygon(Point(x=0, y=0), Point(x=1, y=1))\n",
      "1869044427464 Polygon(Point(x=2, y=2), Point(x=3, y=3))\n"
     ]
    }
   ],
   "source": [
    "p1 = Polygon((0,0), Point(1,1))\n",
    "p2 = Polygon([2, 2], [3, 3])\n",
    "print(id(p1), p1)\n",
    "print(id(p2), p2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 94,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "p1 += p2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 95,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1869044425392 Polygon(Point(x=0, y=0), Point(x=1, y=1), Point(x=2, y=2), Point(x=3, y=3))\n"
     ]
    }
   ],
   "source": [
    "print(id(p1), p1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That worked still, now let's see `append`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 96,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Polygon(Point(x=0, y=0), Point(x=1, y=1), Point(x=2, y=2), Point(x=3, y=3))"
      ]
     },
     "execution_count": 96,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 97,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "p1.append((4, 4))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 98,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Polygon(Point(x=0, y=0), Point(x=1, y=1), Point(x=2, y=2), Point(x=3, y=3), Point(x=4, y=4))"
      ]
     },
     "execution_count": 98,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 99,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "p1.append(Point(5,5))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 104,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1869044425392 Polygon(Point(x=0, y=0), Point(x=1, y=1), Point(x=2, y=2), Point(x=3, y=3), Point(x=4, y=4), Point(x=5, y=5), Point(x=6, y=6), Point(x=7, y=7))\n"
     ]
    }
   ],
   "source": [
    "print(id(p1), p1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`append` seems to be working, now for `extend`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 101,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "p3 = Polygon((6,6), (7,7))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 102,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "p1.extend(p3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 103,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1869044425392 Polygon(Point(x=0, y=0), Point(x=1, y=1), Point(x=2, y=2), Point(x=3, y=3), Point(x=4, y=4), Point(x=5, y=5), Point(x=6, y=6), Point(x=7, y=7))\n"
     ]
    }
   ],
   "source": [
    "print(id(p1), p1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 106,
   "metadata": {},
   "outputs": [],
   "source": [
    "p1.extend([(8,8), Point(9,9)])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 107,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1869044425392 Polygon(Point(x=0, y=0), Point(x=1, y=1), Point(x=2, y=2), Point(x=3, y=3), Point(x=4, y=4), Point(x=5, y=5), Point(x=6, y=6), Point(x=7, y=7), Point(x=8, y=8), Point(x=9, y=9))\n"
     ]
    }
   ],
   "source": [
    "print(id(p1), p1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's see if `insert` works as expected:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 108,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "p1 = Polygon((0,0), (1,1), (2,2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 109,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1869044022576 Polygon(Point(x=0, y=0), Point(x=1, y=1), Point(x=2, y=2))\n"
     ]
    }
   ],
   "source": [
    "print(id(p1), p1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 110,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "p1.insert(1, (100, 100))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 111,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1869044022576 Polygon(Point(x=0, y=0), Point(x=100, y=100), Point(x=1, y=1), Point(x=2, y=2))\n"
     ]
    }
   ],
   "source": [
    "print(id(p1), p1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 112,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "p1.insert(1, Point(50, 50))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 113,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1869044022576 Polygon(Point(x=0, y=0), Point(x=50, y=50), Point(x=100, y=100), Point(x=1, y=1), Point(x=2, y=2))\n"
     ]
    }
   ],
   "source": [
    "print(id(p1), p1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now that we have that working, let's turn our attention to the `__setitem__` method so we can support index and slice assignments:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 114,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Polygon:\n",
    "    def __init__(self, *pts):\n",
    "        if pts:\n",
    "            self._pts = [Point(*pt) for pt in pts]\n",
    "        else:\n",
    "            self._pts = []\n",
    "            \n",
    "    def __repr__(self):\n",
    "        pts_str = ', '.join([str(pt) for pt in self._pts])\n",
    "        return f'Polygon({pts_str})'\n",
    "    \n",
    "    def __len__(self):\n",
    "        return len(self._pts)\n",
    "    \n",
    "    def __getitem__(self, s):\n",
    "        return self._pts[s]\n",
    "    \n",
    "    def __setitem__(self, s, value):\n",
    "        # value could be a single Point (or compatible type) for s an int\n",
    "        # or it could be an iterable of Points if s is a slice\n",
    "        # let's start by handling slices only first\n",
    "        self._pts[s] = [Point(*pt) for pt in value]\n",
    "            \n",
    "    def __add__(self, pt):\n",
    "        if isinstance(pt, Polygon):\n",
    "            new_pts = self._pts + pt._pts\n",
    "            return Polygon(*new_pts)\n",
    "        else:\n",
    "            raise TypeError('can only concatenate with another Polygon')\n",
    "\n",
    "    def append(self, pt):\n",
    "        self._pts.append(Point(*pt))\n",
    "        \n",
    "    def extend(self, pts):\n",
    "        if isinstance(pts, Polygon):\n",
    "            self._pts = self._pts + pts._pts\n",
    "        else:\n",
    "            # assume we are being passed an iterable containing Points\n",
    "            # or something compatible with Points\n",
    "            points = [Point(*pt) for pt in pts]\n",
    "            self._pts = self._pts + points\n",
    "    \n",
    "    def __iadd__(self, pts):\n",
    "        self.extend(pts)\n",
    "        return self\n",
    "    \n",
    "    def insert(self, i, pt):\n",
    "        self._pts.insert(i, Point(*pt))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So, we are only handling slice assignments at this point, not assignments such as `p[0] = Point(0,0)`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 117,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1869044422304 Polygon(Point(x=0, y=0), Point(x=1, y=1), Point(x=2, y=2))\n"
     ]
    }
   ],
   "source": [
    "p = Polygon((0,0), (1,1), (2,2))\n",
    "print(id(p), p)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 118,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "p[0:2] = [(10, 10), (20, 20), (30, 30)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 119,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1869044422304 Polygon(Point(x=10, y=10), Point(x=20, y=20), Point(x=30, y=30), Point(x=2, y=2))\n"
     ]
    }
   ],
   "source": [
    "print(id(p), p)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So this seems to work fine. But this won't yet:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 120,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "type object argument after * must be an iterable, not int",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-120-9794a9715789>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mp\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mPoint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m100\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m100\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32m<ipython-input-114-5972696942b0>\u001b[0m in \u001b[0;36m__setitem__\u001b[1;34m(self, s, value)\u001b[0m\n\u001b[0;32m     20\u001b[0m         \u001b[1;31m# or it could be an iterable of Points if s is a slice\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     21\u001b[0m         \u001b[1;31m# let's start by handling slices only first\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 22\u001b[1;33m         \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_pts\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0ms\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m[\u001b[0m\u001b[0mPoint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0mpt\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mpt\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mvalue\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     23\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     24\u001b[0m     \u001b[1;32mdef\u001b[0m \u001b[0m__add__\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mpt\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32m<ipython-input-114-5972696942b0>\u001b[0m in \u001b[0;36m<listcomp>\u001b[1;34m(.0)\u001b[0m\n\u001b[0;32m     20\u001b[0m         \u001b[1;31m# or it could be an iterable of Points if s is a slice\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     21\u001b[0m         \u001b[1;31m# let's start by handling slices only first\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 22\u001b[1;33m         \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_pts\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0ms\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m[\u001b[0m\u001b[0mPoint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0mpt\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mpt\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mvalue\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     23\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     24\u001b[0m     \u001b[1;32mdef\u001b[0m \u001b[0m__add__\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mpt\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mTypeError\u001b[0m: type object argument after * must be an iterable, not int"
     ]
    }
   ],
   "source": [
    "p[0] = Point(100, 100)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we look at the precise error, we see that our list comprehension is the cause of the error - we fail to correctly handle the case where the value passed in is not an iterable of Points..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 124,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Polygon:\n",
    "    def __init__(self, *pts):\n",
    "        if pts:\n",
    "            self._pts = [Point(*pt) for pt in pts]\n",
    "        else:\n",
    "            self._pts = []\n",
    "            \n",
    "    def __repr__(self):\n",
    "        pts_str = ', '.join([str(pt) for pt in self._pts])\n",
    "        return f'Polygon({pts_str})'\n",
    "    \n",
    "    def __len__(self):\n",
    "        return len(self._pts)\n",
    "    \n",
    "    def __getitem__(self, s):\n",
    "        return self._pts[s]\n",
    "    \n",
    "    def __setitem__(self, s, value):\n",
    "        # value could be a single Point (or compatible type) for s an int\n",
    "        # or it could be an iterable of Points if s is a slice\n",
    "        # we could do this:\n",
    "        if isinstance(s, int):\n",
    "            self._pts[s] = Point(*value)\n",
    "        else:\n",
    "            self._pts[s] = [Point(*pt) for pt in value]\n",
    "            \n",
    "    def __add__(self, pt):\n",
    "        if isinstance(pt, Polygon):\n",
    "            new_pts = self._pts + pt._pts\n",
    "            return Polygon(*new_pts)\n",
    "        else:\n",
    "            raise TypeError('can only concatenate with another Polygon')\n",
    "\n",
    "    def append(self, pt):\n",
    "        self._pts.append(Point(*pt))\n",
    "        \n",
    "    def extend(self, pts):\n",
    "        if isinstance(pts, Polygon):\n",
    "            self._pts = self._pts + pts._pts\n",
    "        else:\n",
    "            # assume we are being passed an iterable containing Points\n",
    "            # or something compatible with Points\n",
    "            points = [Point(*pt) for pt in pts]\n",
    "            self._pts = self._pts + points\n",
    "    \n",
    "    def __iadd__(self, pts):\n",
    "        self.extend(pts)\n",
    "        return self\n",
    "    \n",
    "    def insert(self, i, pt):\n",
    "        self._pts.insert(i, Point(*pt))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This will now work as expected:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 125,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1869044254368 Polygon(Point(x=0, y=0), Point(x=1, y=1), Point(x=2, y=2))\n"
     ]
    }
   ],
   "source": [
    "p = Polygon((0,0), (1,1), (2,2))\n",
    "print(id(p), p)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 126,
   "metadata": {},
   "outputs": [],
   "source": [
    "p[0] = Point(10, 10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 127,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1869044254368 Polygon(Point(x=10, y=10), Point(x=1, y=1), Point(x=2, y=2))\n"
     ]
    }
   ],
   "source": [
    "print(id(p), p)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What happens if we try to assign a single Point to a slice:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 128,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "type object argument after * must be an iterable, not int",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-128-7031fa70fb2b>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mp\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;36m2\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mPoint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m10\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m10\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32m<ipython-input-124-63cbbf5cccab>\u001b[0m in \u001b[0;36m__setitem__\u001b[1;34m(self, s, value)\u001b[0m\n\u001b[0;32m     23\u001b[0m             \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_pts\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0ms\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mPoint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0mvalue\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     24\u001b[0m         \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 25\u001b[1;33m             \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_pts\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0ms\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m[\u001b[0m\u001b[0mPoint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0mpt\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mpt\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mvalue\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     26\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     27\u001b[0m     \u001b[1;32mdef\u001b[0m \u001b[0m__add__\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mpt\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32m<ipython-input-124-63cbbf5cccab>\u001b[0m in \u001b[0;36m<listcomp>\u001b[1;34m(.0)\u001b[0m\n\u001b[0;32m     23\u001b[0m             \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_pts\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0ms\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mPoint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0mvalue\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     24\u001b[0m         \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 25\u001b[1;33m             \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_pts\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0ms\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m[\u001b[0m\u001b[0mPoint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0mpt\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mpt\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mvalue\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     26\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     27\u001b[0m     \u001b[1;32mdef\u001b[0m \u001b[0m__add__\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mpt\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mTypeError\u001b[0m: type object argument after * must be an iterable, not int"
     ]
    }
   ],
   "source": [
    "p[0:2] = Point(10, 10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As expected this will not work. What about assigning an iterable of points to an index:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 130,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "Point co-ordinates must be real numbers.",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-130-db4d01cbd125>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mp\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m[\u001b[0m\u001b[0mPoint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m10\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m10\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mPoint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m20\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m20\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32m<ipython-input-124-63cbbf5cccab>\u001b[0m in \u001b[0;36m__setitem__\u001b[1;34m(self, s, value)\u001b[0m\n\u001b[0;32m     21\u001b[0m         \u001b[1;31m# we could do this:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     22\u001b[0m         \u001b[1;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0ms\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mint\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 23\u001b[1;33m             \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_pts\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0ms\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mPoint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0mvalue\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     24\u001b[0m         \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     25\u001b[0m             \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_pts\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0ms\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m[\u001b[0m\u001b[0mPoint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0mpt\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mpt\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mvalue\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32m<ipython-input-8-aacef18bb1a4>\u001b[0m in \u001b[0;36m__init__\u001b[1;34m(self, x, y)\u001b[0m\n\u001b[0;32m      4\u001b[0m             \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_pt\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0my\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      5\u001b[0m         \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 6\u001b[1;33m             \u001b[1;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'Point co-ordinates must be real numbers.'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      7\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      8\u001b[0m     \u001b[1;32mdef\u001b[0m \u001b[0m__repr__\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mTypeError\u001b[0m: Point co-ordinates must be real numbers."
     ]
    }
   ],
   "source": [
    "p[0] = [Point(10, 10), Point(20, 20)]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This works fine, but the error messages are a bit misleading - we probably should do something about that:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 162,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Polygon:\n",
    "    def __init__(self, *pts):\n",
    "        if pts:\n",
    "            self._pts = [Point(*pt) for pt in pts]\n",
    "        else:\n",
    "            self._pts = []\n",
    "            \n",
    "    def __repr__(self):\n",
    "        pts_str = ', '.join([str(pt) for pt in self._pts])\n",
    "        return f'Polygon({pts_str})'\n",
    "    \n",
    "    def __len__(self):\n",
    "        return len(self._pts)\n",
    "    \n",
    "    def __getitem__(self, s):\n",
    "        return self._pts[s]\n",
    "    \n",
    "    def __setitem__(self, s, value):\n",
    "        # we first should see if we have a single Point\n",
    "        # or an iterable of Points in value\n",
    "        try:\n",
    "            rhs = [Point(*pt) for pt in value]\n",
    "            is_single = False\n",
    "        except TypeError:\n",
    "            # not a valid iterable of Points\n",
    "            # maybe a single Point?\n",
    "            try:\n",
    "                rhs = Point(*value)\n",
    "                is_single = True\n",
    "            except TypeError:\n",
    "                # still no go\n",
    "                raise TypeError('Invalid Point or iterable of Points')\n",
    "        \n",
    "        # reached here, so rhs is either an iterable of Points, or a Point\n",
    "        # we want to make sure we are assigning to a slice only if we \n",
    "        # have an iterable of points, and assigning to an index if we \n",
    "        # have a single Point only\n",
    "        if (isinstance(s, int) and is_single) \\\n",
    "            or isinstance(s, slice) and not is_single:\n",
    "            self._pts[s] = rhs\n",
    "        else:\n",
    "            raise TypeError('Incompatible index/slice assignment')\n",
    "                \n",
    "    def __add__(self, pt):\n",
    "        if isinstance(pt, Polygon):\n",
    "            new_pts = self._pts + pt._pts\n",
    "            return Polygon(*new_pts)\n",
    "        else:\n",
    "            raise TypeError('can only concatenate with another Polygon')\n",
    "\n",
    "    def append(self, pt):\n",
    "        self._pts.append(Point(*pt))\n",
    "        \n",
    "    def extend(self, pts):\n",
    "        if isinstance(pts, Polygon):\n",
    "            self._pts = self._pts + pts._pts\n",
    "        else:\n",
    "            # assume we are being passed an iterable containing Points\n",
    "            # or something compatible with Points\n",
    "            points = [Point(*pt) for pt in pts]\n",
    "            self._pts = self._pts + points\n",
    "    \n",
    "    def __iadd__(self, pts):\n",
    "        self.extend(pts)\n",
    "        return self\n",
    "    \n",
    "    def insert(self, i, pt):\n",
    "        self._pts.insert(i, Point(*pt))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So now let's see if we get better error messages:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 154,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "p1 = Polygon((0,0), (1,1), (2,2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 155,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "Incompatible index/slice assignment",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-155-ed6b2e4597f7>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mp1\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;36m2\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m(\u001b[0m\u001b[1;36m10\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;36m10\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32m<ipython-input-153-5b38eea0109d>\u001b[0m in \u001b[0;36m__setitem__\u001b[1;34m(self, s, value)\u001b[0m\n\u001b[0;32m     39\u001b[0m             \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_pts\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0ms\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mrhs\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     40\u001b[0m         \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 41\u001b[1;33m             \u001b[1;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'Incompatible index/slice assignment'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     42\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     43\u001b[0m     \u001b[1;32mdef\u001b[0m \u001b[0m__add__\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mpt\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mTypeError\u001b[0m: Incompatible index/slice assignment"
     ]
    }
   ],
   "source": [
    "p1[0:2] = (10,10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 156,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "Incompatible index/slice assignment",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-156-77ef903111fc>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mp1\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m[\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m(\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32m<ipython-input-153-5b38eea0109d>\u001b[0m in \u001b[0;36m__setitem__\u001b[1;34m(self, s, value)\u001b[0m\n\u001b[0;32m     39\u001b[0m             \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_pts\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0ms\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mrhs\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     40\u001b[0m         \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 41\u001b[1;33m             \u001b[1;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'Incompatible index/slice assignment'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     42\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     43\u001b[0m     \u001b[1;32mdef\u001b[0m \u001b[0m__add__\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mpt\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mTypeError\u001b[0m: Incompatible index/slice assignment"
     ]
    }
   ],
   "source": [
    "p1[0] = [(0,0), (1,1)]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And the allowed slice/index assignments work as expected:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 157,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "p[0] = Point(100, 100)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 158,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Polygon(Point(x=100, y=100), Point(x=1, y=1), Point(x=2, y=2))"
      ]
     },
     "execution_count": 158,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 159,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "p[0:2] = [(0,0), (1,1), (2,2)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 160,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Polygon(Point(x=0, y=0), Point(x=1, y=1), Point(x=2, y=2), Point(x=2, y=2))"
      ]
     },
     "execution_count": 160,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And if we try to replace with bad Point data:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 161,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "Point co-ordinates must be real numbers.",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-161-37ccf6d72caa>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mp\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m2\u001b[0m\u001b[1;33m+\u001b[0m\u001b[1;36m2j\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32m<ipython-input-124-63cbbf5cccab>\u001b[0m in \u001b[0;36m__setitem__\u001b[1;34m(self, s, value)\u001b[0m\n\u001b[0;32m     21\u001b[0m         \u001b[1;31m# we could do this:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     22\u001b[0m         \u001b[1;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0ms\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mint\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 23\u001b[1;33m             \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_pts\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0ms\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mPoint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0mvalue\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     24\u001b[0m         \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     25\u001b[0m             \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_pts\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0ms\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m[\u001b[0m\u001b[0mPoint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0mpt\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mpt\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mvalue\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32m<ipython-input-8-aacef18bb1a4>\u001b[0m in \u001b[0;36m__init__\u001b[1;34m(self, x, y)\u001b[0m\n\u001b[0;32m      4\u001b[0m             \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_pt\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0my\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      5\u001b[0m         \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 6\u001b[1;33m             \u001b[1;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'Point co-ordinates must be real numbers.'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      7\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      8\u001b[0m     \u001b[1;32mdef\u001b[0m \u001b[0m__repr__\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mTypeError\u001b[0m: Point co-ordinates must be real numbers."
     ]
    }
   ],
   "source": [
    "p[0] = (0, 2+2j)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We also get a better error message."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Lastly let's see how we would implement the `del` keyword and the `pop` method."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Recall how the `del` keyword works for a list:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 163,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "l = [1, 2, 3, 4, 5]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 164,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "del l[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 165,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[2, 3, 4, 5]"
      ]
     },
     "execution_count": 165,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "l"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 166,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "del l[0:2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 167,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[4, 5]"
      ]
     },
     "execution_count": 167,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "l"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 168,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "del l[-1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 169,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[4]"
      ]
     },
     "execution_count": 169,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "l"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So, `del` works with indices (positive or negative) and slices too. We'll do the same:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 180,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Polygon:\n",
    "    def __init__(self, *pts):\n",
    "        if pts:\n",
    "            self._pts = [Point(*pt) for pt in pts]\n",
    "        else:\n",
    "            self._pts = []\n",
    "            \n",
    "    def __repr__(self):\n",
    "        pts_str = ', '.join([str(pt) for pt in self._pts])\n",
    "        return f'Polygon({pts_str})'\n",
    "    \n",
    "    def __len__(self):\n",
    "        return len(self._pts)\n",
    "    \n",
    "    def __getitem__(self, s):\n",
    "        return self._pts[s]\n",
    "    \n",
    "    def __setitem__(self, s, value):\n",
    "        # we first should see if we have a single Point\n",
    "        # or an iterable of Points in value\n",
    "        try:\n",
    "            rhs = [Point(*pt) for pt in value]\n",
    "            is_single = False\n",
    "        except TypeError:\n",
    "            # not a valid iterable of Points\n",
    "            # maybe a single Point?\n",
    "            try:\n",
    "                rhs = Point(*value)\n",
    "                is_single = True\n",
    "            except TypeError:\n",
    "                # still no go\n",
    "                raise TypeError('Invalid Point or iterable of Points')\n",
    "        \n",
    "        # reached here, so rhs is either an iterable of Points, or a Point\n",
    "        # we want to make sure we are assigning to a slice only if we \n",
    "        # have an iterable of points, and assigning to an index if we \n",
    "        # have a single Point only\n",
    "        if (isinstance(s, int) and is_single) \\\n",
    "            or isinstance(s, slice) and not is_single:\n",
    "            self._pts[s] = rhs\n",
    "        else:\n",
    "            raise TypeError('Incompatible index/slice assignment')\n",
    "                \n",
    "    def __add__(self, pt):\n",
    "        if isinstance(pt, Polygon):\n",
    "            new_pts = self._pts + pt._pts\n",
    "            return Polygon(*new_pts)\n",
    "        else:\n",
    "            raise TypeError('can only concatenate with another Polygon')\n",
    "\n",
    "    def append(self, pt):\n",
    "        self._pts.append(Point(*pt))\n",
    "        \n",
    "    def extend(self, pts):\n",
    "        if isinstance(pts, Polygon):\n",
    "            self._pts = self._pts + pts._pts\n",
    "        else:\n",
    "            # assume we are being passed an iterable containing Points\n",
    "            # or something compatible with Points\n",
    "            points = [Point(*pt) for pt in pts]\n",
    "            self._pts = self._pts + points\n",
    "    \n",
    "    def __iadd__(self, pts):\n",
    "        self.extend(pts)\n",
    "        return self\n",
    "    \n",
    "    def insert(self, i, pt):\n",
    "        self._pts.insert(i, Point(*pt))\n",
    "        \n",
    "    def __delitem__(self, s):\n",
    "        del self._pts[s]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 181,
   "metadata": {},
   "outputs": [],
   "source": [
    "p = Polygon(*zip(range(6), range(6)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 182,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Polygon(Point(x=0, y=0), Point(x=1, y=1), Point(x=2, y=2), Point(x=3, y=3), Point(x=4, y=4), Point(x=5, y=5))"
      ]
     },
     "execution_count": 182,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 183,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "del p[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 184,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Polygon(Point(x=1, y=1), Point(x=2, y=2), Point(x=3, y=3), Point(x=4, y=4), Point(x=5, y=5))"
      ]
     },
     "execution_count": 184,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 185,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "del p[-1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 186,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Polygon(Point(x=1, y=1), Point(x=2, y=2), Point(x=3, y=3), Point(x=4, y=4))"
      ]
     },
     "execution_count": 186,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 187,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "del p[0:2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 188,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Polygon(Point(x=3, y=3), Point(x=4, y=4))"
      ]
     },
     "execution_count": 188,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, we just have to implement `pop`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 189,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Polygon:\n",
    "    def __init__(self, *pts):\n",
    "        if pts:\n",
    "            self._pts = [Point(*pt) for pt in pts]\n",
    "        else:\n",
    "            self._pts = []\n",
    "            \n",
    "    def __repr__(self):\n",
    "        pts_str = ', '.join([str(pt) for pt in self._pts])\n",
    "        return f'Polygon({pts_str})'\n",
    "    \n",
    "    def __len__(self):\n",
    "        return len(self._pts)\n",
    "    \n",
    "    def __getitem__(self, s):\n",
    "        return self._pts[s]\n",
    "    \n",
    "    def __setitem__(self, s, value):\n",
    "        # we first should see if we have a single Point\n",
    "        # or an iterable of Points in value\n",
    "        try:\n",
    "            rhs = [Point(*pt) for pt in value]\n",
    "            is_single = False\n",
    "        except TypeError:\n",
    "            # not a valid iterable of Points\n",
    "            # maybe a single Point?\n",
    "            try:\n",
    "                rhs = Point(*value)\n",
    "                is_single = True\n",
    "            except TypeError:\n",
    "                # still no go\n",
    "                raise TypeError('Invalid Point or iterable of Points')\n",
    "        \n",
    "        # reached here, so rhs is either an iterable of Points, or a Point\n",
    "        # we want to make sure we are assigning to a slice only if we \n",
    "        # have an iterable of points, and assigning to an index if we \n",
    "        # have a single Point only\n",
    "        if (isinstance(s, int) and is_single) \\\n",
    "            or isinstance(s, slice) and not is_single:\n",
    "            self._pts[s] = rhs\n",
    "        else:\n",
    "            raise TypeError('Incompatible index/slice assignment')\n",
    "                \n",
    "    def __add__(self, pt):\n",
    "        if isinstance(pt, Polygon):\n",
    "            new_pts = self._pts + pt._pts\n",
    "            return Polygon(*new_pts)\n",
    "        else:\n",
    "            raise TypeError('can only concatenate with another Polygon')\n",
    "\n",
    "    def append(self, pt):\n",
    "        self._pts.append(Point(*pt))\n",
    "        \n",
    "    def extend(self, pts):\n",
    "        if isinstance(pts, Polygon):\n",
    "            self._pts = self._pts + pts._pts\n",
    "        else:\n",
    "            # assume we are being passed an iterable containing Points\n",
    "            # or something compatible with Points\n",
    "            points = [Point(*pt) for pt in pts]\n",
    "            self._pts = self._pts + points\n",
    "    \n",
    "    def __iadd__(self, pts):\n",
    "        self.extend(pts)\n",
    "        return self\n",
    "    \n",
    "    def insert(self, i, pt):\n",
    "        self._pts.insert(i, Point(*pt))\n",
    "        \n",
    "    def __delitem__(self, s):\n",
    "        del self._pts[s]\n",
    "        \n",
    "    def pop(self, i):\n",
    "        return self._pts.pop(i)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 190,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "p = Polygon(*zip(range(6), range(6)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 191,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Polygon(Point(x=0, y=0), Point(x=1, y=1), Point(x=2, y=2), Point(x=3, y=3), Point(x=4, y=4), Point(x=5, y=5))"
      ]
     },
     "execution_count": 191,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 192,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Point(x=1, y=1)"
      ]
     },
     "execution_count": 192,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p.pop(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 193,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Polygon(Point(x=0, y=0), Point(x=2, y=2), Point(x=3, y=3), Point(x=4, y=4), Point(x=5, y=5))"
      ]
     },
     "execution_count": 193,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
